﻿package physic
{
	import events.MoveEvent;
	import events.PhysicEvent;
	import events.PlateformeEvent;
	import fab.Utils;
	import flash.events.EventDispatcher;
	import flash.events.TimerEvent;
	import flash.utils.Timer;
	import flash.geom.Point;
	import fab.geom.*;
	import bipede.membres.Person;
	import bipede.membres.PhysicJoint;
	import bipede.membres.Pied;
	import bipede.membres.Squelette;
	import physic.elements.Handle;
	//import flash.geom.Rectangle;
	import flash.display.DisplayObject;
		import flash.display.Bitmap;
	import flash.display.BitmapData;	
	import flash.geom.Rectangle;
	import flash.geom.Matrix;
	
	import fl.ik.*;
	
	// TODO extends Timer comme AnimationTimer !!
	import flash.events.TimerEvent;
	import flash.utils.Timer;
	public class Engine extends Timer
	{
		static public var PESANTEUR:Point = new Point( 0, 5 );
		static public var FROTT_AIR:Number = .05;
		
		static public var softbodys:Vector.<SoftBody> = new Vector.<SoftBody>();
		static public var obstacles:Vector.<Obstacle> = new Vector.<Obstacle>();
		static public var handles:Vector.<Handle> = new Vector.<Handle>();
		static public var started:Boolean = false;
		static public var instance:Engine;
		static public var zone_obstacles:BitmapData; 
		static public var espace:DisplayObject;
		
		public var MSPF:int = 40;// millisecond per frames
		
		public function Engine()
		{
			super( MSPF, 0 );
			addEventListener( TimerEvent.TIMER, _tick );			
			addEventListener( TimerEvent.TIMER_COMPLETE, _fin );				
		}
		static public function getInstance():Engine
		{
			return ( instance != null )?instance:new Engine();
		}		
		private function _tick(event:TimerEvent):void
		{
			trace( "Engine TICK\r");
			
			if ( softbodys.length == 0 ) stop();
			
			// bilan
			
			var somme_force:Point = PESANTEUR.clone();// m*a = g + R + kV
				
			for each ( var softbody:SoftBody in softbodys )
			{
				var squelette:Squelette = softbody.display as Squelette;
				//var person:Person= softbody.display as Person;

				// TODO : mauvais comportement avec le mirroring
				//if ( softbody.vitesse.length > 5 )
					//somme_force = somme_force.subtract( new Point( softbody.vitesse.x * FROTT_AIR, softbody.vitesse.y * FROTT_AIR ) );
					
				/* reactions
				for each ( var reaction:Reaction in softbody.reactions )
				{
					somme_force.offset( reaction.x, reaction.y );//PESANTEUR.clone()
				}
				if ( softbody.reactions.length > 0 ) somme_force = new Point();// somme_force.offset( -PESANTEUR.x, -PESANTEUR.y );
				somme_force.normalize( somme_force.length / softbody.masse );
				*/
				
				// derivation
				
				softbody.acceleration = somme_force;// PESANTEUR.subtract( new Point( softbody.vitesse.x * FROTT_AIR, softbody.vitesse.y * FROTT_AIR ) );
				softbody.vitesse = softbody.vitesse.add( softbody.acceleration );
				
				// position
				
				var current:Point = squelette.place.clone();// new Point( squelette.x, squelette.y );
				var target:Point = current.add( softbody.vitesse );

				
				// mirroring
				
				var zone:Rectangle = TheWalker.zone_utile;
				if ( !zone.containsPoint( target ) )
				{
					if ( target.x < zone.left ) target.x += zone.width;
					if ( target.x > zone.right ) target.x -= zone.width;
					if ( target.y < zone.top ) target.y += zone.height;
					if ( target.y > zone.bottom ) target.y -= zone.height;
				}
				
				// hittest Handles
				
				for each ( var handle:Handle in handles )
				{
					// TODO handle.place est dans Engine.espace ?
					var circle:Circle;
					circle = squelette.main_droite.portee;
					circle.update( circle.centre.add( squelette.place ) );// faire un Utils.circleTransform ?
					if ( circle.containsPoint( handle.place ) )
					{
						var c1:Contrainte = squelette.create_contrainte( handle.place, "bone_mainD", "bone_epauleD" );
					}
					
					circle = squelette.main_gauche.portee;
					circle.update( circle.centre.add( squelette.place ) );// faire un Utils.circleTransform ?
					if ( circle.containsPoint( handle.place ) )
					{
						var c1:Contrainte = squelette.create_contrainte( handle.place, "bone_mainG", "bone_epauleG" );
					}					
				}
			
				// hittest Obstacles
			
				if ( current.y < target.y )// sens de la descente
				{
					var target_pieds_max:Point = Point.interpolate( target, current, 1 + squelette.data.distance_support_max / Point.distance( current, target ) );
					var touch_segment:Segment = new Segment( current, target_pieds_max );
					
					// TODO : attention, hitSegment marche sur Obstacle.liste, et pas Engine.obstacles !!!??
					
					var hitted_obstacles:Array = Obstacle.hitSegment( touch_segment );
					if (  hitted_obstacles.length != 0 )
					{
						squelette.dispatchEvent( new MoveEvent( MoveEvent.HIT_OBSTACLE, hitted_obstacles ) );// -> atterrissage !
						
						unRegisterSoftBody( softbody.display )
						
						// arreter Engine ?
						//stop();
						break;
					}
				}
				squelette.dispatchEvent( new MoveEvent( MoveEvent.MOVING, target ) );
			}
		} 
		public function _fin(e:TimerEvent):void
		{
		}	

		override public function start():void
		{
			for each ( var softbody:SoftBody in softbodys )
			{
				var sujet:Person = softbody.display as Person;
				sujet.addEventListener( MoveEvent.MOVING, sujet.moving );
			}
			//dispatchEvent( new PhysicEvent( PhysicEvent.START ) );
			started = true;
			reset();			
			super.start();			
		}	
		override public function stop():void
		{
			started = false;
			super.stop();
			//dispatchEvent( new PhysicEvent( PhysicEvent.STOP ) );
			for each ( var softbody:SoftBody in softbodys )
			{
				var sujet:Person = softbody.display as Person;
				sujet.removeEventListener( MoveEvent.MOVING, sujet.moving );
			}			
		}
		public function pause():void
		{
			if ( running ) stop();
			else if ( started ) start();
			//dispatchEvent( new PhysicEvent( PhysicEvent.PAUSE ) );
		}		
		static public function next(event=null):void
		{
			instance._tick( new TimerEvent(TimerEvent.TIMER) );
		}		
		static public function registerSoftBody( _display:DisplayObject ):SoftBody
		{
			if ( instance == null ) instance = new Engine();
			var softbody:SoftBody = new SoftBody( _display );
			softbodys.push( softbody );
			return softbody;
		}
		static public function unRegisterSoftBody( _display:DisplayObject ):void
		{
			for each ( var softbody:SoftBody in softbodys )
			{
				var sujet:Person = softbody.display as Person;
				if ( softbody.display == _display )
				{
					softbodys.splice( softbodys.indexOf( softbody ), 1 );
				}
			}
		}		
		static public function registerHandle( _display:Handle ):void
		{
			if ( instance == null ) instance = new Engine();
			handles.push( _display );
			_display.x = int( _display.x );
			_display.y = int( _display.y );
		}		
		static public function registerObstacle( _display:DisplayObject, p1:Point, p2:Point ):Obstacle
		{
			if ( instance == null ) instance = new Engine();
			var obstacle:Obstacle = new Obstacle( _display, p1, p2 );
			obstacles.push( obstacle );
			_display.x = int( _display.x );
			_display.y = int( _display.y );
			//traceZoneObstacle();
			
			return obstacle;
		}
		static public function hit_pied( pied:Pied ):Obstacle
		{
			var talon:Point = pied.squelette.localToGlobal( pied.talon );// TODO PhysicScene(display object).globalToLocal( ... )
			var orteil:Point = pied.squelette.localToGlobal( pied.orteil );
			
			talon = espace.globalToLocal( talon );// TODO PhysicScene(display object).globalToLocal( ... )
			orteil = espace.globalToLocal( orteil );
			
			//Drawer.drawPoint(Main.drawings.graphics, talon, 0x00FF00);
			//Drawer.drawPoint(Main.drawings.graphics, orteil, 0x0000FF);
			
			for each ( var obstacle:Obstacle in obstacles )
			{
				if ( obstacle.display.hitTestPoint( talon.x, talon.y, true ) || obstacle.display.hitTestPoint( orteil.x, orteil.y, true ) )
				{
					return obstacle;
				}
			}
			return null;
		}			
	}
}